﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Common.Util
{
    public sealed class DllHelper : IDisposable
    {
        private IntPtr libr;// 一个非托管资源的win32句柄

        // 导入引擎dll
        [DllImport("Kernel32.dll", SetLastError = true)]
        private static extern IntPtr LoadLibrary(string path);
        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.None)]
        private static extern IntPtr GetProcAddress(IntPtr lib, string FunName);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool FreeLibrary(IntPtr lib);

        // 加载DLL
        public DllHelper(string path)
        {
            libr = LoadLibrary(path);
            if (libr == IntPtr.Zero)
            {
                throw new DllNotFoundException("所查找的dll没有找到或dll句柄已关闭!" + path);
            }
        }

        // 将要执行的函数转换为委托
        public Delegate Invoke(string funName, Type type)
        {
            IntPtr addr = GetProcAddress(libr, funName);
            if (addr == IntPtr.Zero)
            {
                throw new EntryPointNotFoundException("入口函数没找到!");
            }
            else
            {
                return (Delegate)Marshal.GetDelegateForFunctionPointer(addr, type);
            }
        }

        // 调用析构函数卸载DLL
        ~DllHelper()
        {
            Dispose(false);
        }

        // 显式调用来关闭非托管资源的句柄
        public void Dispose()
        {
            // 因为对象的资源被显式的清理，所以在这里阻止垃圾收集器调用Finalize方法
            GC.SuppressFinalize(this);

            // 进行实际的资源清理工作
            Dispose(true);
        }


        // 实际的清理工作，Finalize和Dispose方法都要调用该方法。
        // 因为该类是一个密封类，所以该方法被定义为一个私有方法;
        // 如果需要派生，则该方法应为受保护的虚方法，在派生类中只需要重写这个方法即可，然后在finally中再调用基类的这个方法。
        private void Dispose(Boolean disposing)
        {
            // 同步那些同时调用Dispose方法的线程
            lock (this)
            {
                if (disposing)
                {
                    // 对象正在被显式执行释放/关闭而非执行终止化。因此在该if语句中访问那些引用其他对象的
                    // 字段是安全的，因为这些对象的Finalize方法还没有被调用

                }
                // 对象正在被释放/关闭、或者被执行终止化
                if (libr != IntPtr.Zero)
                {
                    // 如果libr有效，那么关闭非托管资源
                    bool r = FreeLibrary(libr);
                    if (!r)
                    {
                        throw new Exception("清理资源错误！");
                    }

                    // 防止多次调用FreeLibrary
                    libr = IntPtr.Zero;
                }
            }
        }

    }
}
